package com.xzzz.irda.auth.core.filter;

import com.xzzz.irda.auth.core.authorization.AuthContext;
import com.xzzz.irda.auth.core.AuthBaseConfiguration;
import com.xzzz.common.base.util.json.JsonUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.filter.GenericFilterBean;

import javax.annotation.PostConstruct;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * 代理拦截器
 * <p>
 * 注入方式: 由 AuthBaseConfiguration 扫描注入
 *
 * @author wangyf
 * @see AuthBaseConfiguration
 * 
 */
@Slf4j
public class AuthFilterProxy extends GenericFilterBean {

    /**
     * 公用基础配置
     */
    private AuthBaseConfiguration authBaseConfiguration;

    /**
     * 防火墙对象
     */
    private HttpFirewall httpFirewall = new HttpFirewall();

    /**
     * 过滤器集合
     */
    private final List<AuthFilter> filters = new ArrayList<>();

    /**
     * 构造方法, 由外部传入配置对象
     *
     * @param authBaseConfiguration 基础配置对象
     */
    public AuthFilterProxy(AuthBaseConfiguration authBaseConfiguration) {
        this.authBaseConfiguration = authBaseConfiguration;
    }

    /**
     * 注入拦截器
     *
     * @param filters 全部注入到IOC的 {@link AuthFilter} 实现类
     */
    @Autowired
    public void initFilters(List<AuthFilter> filters) {
        this.filters.addAll(filters);
    }

    /**
     * 执行过滤器, 是 Servlet 过滤器中的一环
     *
     * @param servletRequest  request
     * @param servletResponse response
     * @param servletChain    servlet 的过滤器链, 并不是授权模块过滤器链
     * @throws IOException      IOException
     * @throws ServletException ServletException
     */
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain servletChain) throws IOException, ServletException {
        log.debug("[AUTHORIZ] ================================ 代理过滤器 [开始] ================================");
        try {
            // 执行过滤器方法
            this.doFilterInternal(servletRequest, servletResponse, servletChain);
        } catch (Exception e) {
            log.debug("[AUTHORIZ] **Proxy** >> 代理过滤器执行异常: {}", e.getMessage());
            // 执行时出现异常, 在此捕获并统一处理
            authBaseConfiguration.getAuthFailureHandler()
                    .onAuthenticationFailure(servletRequest, servletResponse, e);
        } finally {
            // 无论执行成功与否都需要清空上下文
            AuthContext.removeContext();
            log.debug("[AUTHORIZ] **Proxy** << 代理过滤器: response 清空上下文: {}", JsonUtil.toJson(AuthContext.getContext()));
        }
        log.debug("[AUTHORIZ] ================================ 代理过滤器 [结束] ================================");
    }


    /**
     * 执行本过滤器逻辑
     *
     * @param servletRequest  request
     * @param servletResponse response
     * @param servletChain    servlet 的过滤器链, 并不是授权模块过滤器链
     * @throws IOException      IOException
     * @throws ServletException ServletException
     */
    private void doFilterInternal(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain servletChain) throws IOException, ServletException {

        // 如果授权过滤器集合为空, 则直接执行原生过滤器
        if (filters.size() == 0) {
            servletChain.doFilter(servletRequest, servletResponse);
            return;
        }

        /*
         1. 执行防火墙逻辑
         */
        httpFirewall(servletRequest);

        /*
         2.
         创建虚拟过滤器链, 执行责任链模式调用过滤器, 此处参考了 Spring Security 的设计
         此处创建对象执行时只是了保证过滤器链调用的线程安全, 该对象内部没有多余参数, 且只做一次循环
         所以该对象实际大小很小, 所以也不必显式的设置为 null 来帮助 gc
         */
        VirtualFilterChain virtualFilterChain = new VirtualFilterChain(filters);
        virtualFilterChain.doFilter(servletRequest, servletResponse);
        /*
         虚拟过滤器链执行完毕后交由原生过滤器继续执行
         */
        servletChain.doFilter(servletRequest, servletResponse);

        log.debug("[AUTHORIZ] **Proxy** >> 原生过滤器: request  请求结束");
    }


    /**
     * 执行防火墙, 校验请求是否合法
     *
     * @param request 请求
     */
    void httpFirewall(ServletRequest request) {
        log.debug("[AUTHORIZ] ── 防火墙");
        httpFirewall.wall(request);
    }

    /**
     * 每次请求创建虚拟过滤器链, 用来完成责任链调用
     */
    private static final class VirtualFilterChain implements AuthFilterChain {
        // 过滤器集合
        private final List<AuthFilter> additionalFilters;
        // 过滤器链长度
        private final int size;
        // 当前执行位置
        private int currentPosition;

        /**
         * 构造虚拟过滤器链
         *
         * @param additionalFilters 过滤器集合
         */
        public VirtualFilterChain(List<AuthFilter> additionalFilters) {
            this.additionalFilters = additionalFilters;
            this.size = additionalFilters.size();
            this.currentPosition = 0;
        }

        /**
         * 执行虚拟过滤器
         *
         * @param request  request request
         * @param response response response
         * @throws IOException      IOException
         * @throws ServletException ServletException
         */
        @Override
        public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
            // 过滤器链执行完毕，返回
            if (this.currentPosition == this.size) {
                return;
            }
            this.currentPosition++;
            AuthFilter nextFilter = this.additionalFilters.get(this.currentPosition - 1);
            nextFilter.doFilter(request, response, this);
        }
    }


    @PostConstruct
    public void init() {
        this.filterOrderSort();
    }

    /**
     * 拦截器的排序, order 小的优先执行
     */
    private void filterOrderSort() {
        this.filters.sort((e, h) -> e.getOrder() > h.getOrder() ? 1 : -1);
    }


}
